package com.citusdata.elven.linearRoad;

import com.citusdata.elven.linearRoad.FullPosition;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.ConcurrentNavigableMap;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;


// Helper class to track accidents and their occurrence times.
class Accident {
  private volatile int startTime;
  private volatile int endTime = Integer.MAX_VALUE-1;

  protected static Accident newInstance(Accident accident) {
    Accident copy = new Accident();
    copy.startTime = accident.startTime;
    copy.endTime = accident.endTime;

    return copy;
  }

  protected void recordStartTime(int startTime) {
    this.startTime = startTime;
  }

  protected void recordEndTime(int endTime) {
    this.endTime = endTime;
  }

  protected boolean active(int minute) {
    return (minute > startTime && minute <= (endTime+1));
  }

  protected boolean over(int minute) {
    return (minute > (endTime+1));
  }
}


class AccidentDetection {
  private static final int STOPS_FOR_ACCIDENT = 2;

  private Map stoppedVehicles = new HashMap();
  private Map stoppedPositions = new HashMap();
  private ConcurrentSkipListMap accidents = new ConcurrentSkipListMap();

  protected synchronized void recordStoppedVehicle(int minute, int vid,
                                                   FullPosition position) {
    stoppedVehicles.put(vid, position);

    int stoppedCount = 1;
    if (stoppedPositions.containsKey(position)) {
      int prevCount = ((Integer) stoppedPositions.get(position)).intValue();
      stoppedCount = prevCount+1;
    }
    stoppedPositions.put(position, stoppedCount);

    if (stoppedCount == STOPS_FOR_ACCIDENT) {
      Accident accident = new Accident();
      accident.recordStartTime(minute);
      accidents.put(position, accident);
    }
  }

  protected synchronized void clearStoppedVehicle(int minute, int vid) {
    FullPosition position = (FullPosition) stoppedVehicles.get(vid);
    stoppedVehicles.remove(vid);

    int prevCount = ((Integer) stoppedPositions.get(position)).intValue();
    int stoppedCount = prevCount-1;
    if (stoppedCount < 0) {
      // An assertion is more appropriate here; however, due to integration
      // problems with JUnit, we are throwing an exception.
      throw new IllegalStateException("Stop/move mismatch in stoppedVehicles");
    }

    if (stoppedCount == 0) {
      stoppedPositions.remove(position);
    } else {
      stoppedPositions.put(position, stoppedCount);
    }

    if (prevCount == STOPS_FOR_ACCIDENT) {
      Accident accident = (Accident) accidents.get(position);
      accident.recordEndTime(minute);
    }
  }

  protected boolean accidentExists(int minute, FullPosition begin, FullPosition end) {
    ConcurrentNavigableMap segmentAccidents = accidents.subMap(begin, end);
    
    for (Iterator iter = segmentAccidents.values().iterator(); iter.hasNext(); ) {
      Accident accident = (Accident) iter.next();
      if (accident.active(minute)) {
        return true;
      } else if (accident.over(minute)) {
        // If the put operation on accidents occurs at exactly the same time as
        // the following remove operation, an old accident will linger in the
        // map. This is OK.
        iter.remove();
      }
    }
    
    return false;
  }
}